/*INCLUDES *******************************************************************/
#include "ses_alarm_clock.h"
#include "ses_led.h"


/* DEFINES & MACROS **********************************************************/
#define HOUR_IN_MS 				3600000
#define MIN_IN_MS 				60000
#define RED_FLASH_INTERVALL		300
#define GREEN_FLASH_INTERVALL 	1000
#define HOURS_A_DAY_IN_MS 		(3600000*24)
#define MINUTES_A_HOUR_IN_MS	(60000 * 60)


/* FUNCTION PROTOTYPES *******************************************************/
void startRedFlash();
void stopRedFlash();
uint8_t getHours(uint32_t timeSet);
uint8_t getMinutes(uint32_t timeSet);
void setHours(uint32_t *timeSet, uint8_t hour);
void setMinutes(uint32_t *timeSet, uint8_t hour);


/* FUNCTION DEFINITIONS ******************************************************/

void aClock_init(Fsm *fsm) {
	scheduler_init();
	displayPrint_init();
	button_init();
	led_allInit();
	fsm->alarmSet = 0;
	fsm->timeSet = 0;

	//go to first state
	fsm->state = aClock_setHH;

	//update display
	printf("%lu:00  \nRotary: HH++\nJoystick: next:",
				fsm->timeSet / HOUR_IN_MS);
}

void aClock_setHH(Fsm *fsm, const Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//increment hours
		fsm->timeSet += HOUR_IN_MS;

		//calculate overflow and start at 0h if hour value is bigger than 23
		if( getHours( fsm->timeSet) > 23 ){
			setHours( &(fsm->timeSet), 0 );
		}

		//display time
		displayPrint_setCursor(0, 0);
		//check if result has one or two digits
		if (fsm->timeSet < HOUR_IN_MS * 10) { //TODO magic number, abfrage auf zweistellige stundenzahl
			printf("0%lu:00  ", fsm->timeSet / HOUR_IN_MS);
		} else {
			printf("%lu:00  ", fsm->timeSet / HOUR_IN_MS);
		}
		break;

	case JS_BUTTON:
		//go to next state
		fsm->state = aClock_setMM;

		//update display
		displayPrint_init();
		printf("%lu:00\nRotary: MM++\nJoystick: next:",
				fsm->timeSet / HOUR_IN_MS);
		break;

	default:
		break;
	}
}

void aClock_setMM(Fsm *fsm, const Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//increment minutes
		fsm->timeSet += MIN_IN_MS;

		//calculate overflow and start at 0m if minute value is bigger than 59
		if( getMinutes( fsm->timeSet ) > 59 ) {
			setMinutes( &(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);
		//check if result needs one or two digits
		if (fsm->timeSet % 3600000 < MIN_IN_MS * 10) { //TODO magic number, abfrage auf zweistellige minutenzahl
			printf("%lu:0%lu", fsm->timeSet / HOUR_IN_MS,
					(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		} else {
			printf("%lu:%lu", fsm->timeSet / HOUR_IN_MS,
					(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		}
		break;

	case JS_BUTTON:
		//set current time
		scheduler_setTime(fsm->timeSet);
		fsm->timeSet = 0;

		//go to next state normal mode
		fsm->state = aClock_normalMode;
		displayPrint_init();

		//update display
		type_timeRTC *time;
		scheduler_getTimeRTC(time);
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		break;

	default:
		break;
	}
}

void aClock_normalMode(Fsm *fsm, const Event *event) {
	type_timeRTC *time;

	switch (event->signal) {
	case SECOND_TIMER:
		//update the display and toggle green led
		scheduler_getTimeRTC(time);
		displayPrint_init();
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		led_greenToggle();
		break;

	case JS_BUTTON:
		//go to next state and set alarm hours
		fsm->state = aClock_setAlarmHH;
		displayPrint_init();
		fsm->timeSet = 0;
		break;

	case ROT_BUTTON:
		//turn on alarm go to next state
		fsm->state = aClock_alarmMode;
		displayPrint_init();
		led_yellowOn();
		fsm->alarmSet = 1;

		//update display
		scheduler_getTimeRTC(time);
		printf("%u:%u:%u \nRotary AL Off", time->hour,
				time->minute, time->second);
		break;

	default:
		break;
	}
}

void aClock_alarmMode(Fsm *fsm, const Event *event) {
	type_timeRTC *time;

	switch (event->signal) {
	case ALARM_TIMER:
		//its alarm time, start buzzing and flash red led
		led_yellowOff();
		fsm->state = aClock_buzzing;
		displayPrint_init();
		scheduler_add(startRedFlash, 0);
		buzzer_start();

		//update display
		displayPrint_init();
		printf("%u:%u:%u\nAUFWACHEN!!!!\nAny Key To Stop", time->hour,
				time->minute, time->second);
		break;

	case SECOND_TIMER:
		//update display and toggle green led
		scheduler_getTimeRTC(time);
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		led_greenToggle();
		break;

	case ROT_BUTTON:
		//go back to normal mode
		fsm->state = aClock_normalMode;
		displayPrint_init();

		//turn off alarm
		led_yellowOff();
		fsm->alarmSet = 0;

		//update display
		scheduler_getTimeRTC(time);
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		break;

	default:
		break;
	}


}
void aClock_setAlarmHH(Fsm *fsm, const Event *event) {
	switch (event->signal) {
		case ROT_BUTTON:
			//increment hours
			fsm->timeSet += HOUR_IN_MS;

			//calculate overflow and start at 0h if hour value is bigger than 23
			if( getHours( fsm->timeSet) > 23 ){
				setHours( &(fsm->timeSet), 0 );
			}

			//display time
			displayPrint_setCursor(0, 0);
			//check if there are one or two digits needed
			if (fsm->timeSet < HOUR_IN_MS * 10) { //TODO magic number, abfrage auf zweistellige stundenzahl
				printf("0%lu:00", fsm->timeSet / HOUR_IN_MS);
			} else {
				printf("%lu:00", fsm->timeSet / HOUR_IN_MS);
			}
			break;

		case JS_BUTTON:
			//go to next state for setting alarm minutes
			fsm->state = aClock_setAlarmMM;

			//update display
			displayPrint_init();
			printf("%lu:%lu Alarm\nRotary: MM++\nJoystick: next:",
					fsm->timeSet / HOUR_IN_MS,
					(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
			break;

		default:
			break;
		}
}

void aClock_setAlarmMM(Fsm *fsm, const Event *event) {
	switch (event->signal) {
	case ROT_BUTTON:
		//incement minutes
		fsm->timeSet += MIN_IN_MS;

		//calculate overflow and start at 0m if minute value is bigger than 59
		if( getMinutes( fsm->timeSet ) > 59 ) {
			setMinutes( &(fsm->timeSet), 0);
		}

		//display time
		displayPrint_setCursor(0, 0);
		//check for one or two digits to display
		if (fsm->timeSet % 3600000 < MIN_IN_MS * 10) { //TODO magic number, abfrage auf zweistellige minutenzahl
			printf("%lu:0%lu", fsm->timeSet / HOUR_IN_MS,
					(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		} else {
			printf("%lu:%lu", fsm->timeSet / HOUR_IN_MS,
					(fsm->timeSet % HOUR_IN_MS) / MIN_IN_MS);
		}
		break;

	case JS_BUTTON:
		//go to alarm mode
		fsm->state = aClock_alarmMode;
		fsm->alarmSet = 1;
		led_yellowOn();

		//update display
		displayPrint_init();
		type_timeRTC *time;
		scheduler_getTimeRTC(time);
		printf("%u:%u:%u \nRotary AL Off", time->hour,
				time->minute, time->second);
		break;


	default:
		break;
	}
}
void aClock_buzzing(Fsm *fsm, const Event *event) {
	type_timeRTC *time;

	switch(event->signal){
	case ROT_BUTTON:
		//stop the buzzing and return to normal mode
		stopRedFlash();
		led_yellowOff();
		buzzer_stop();
		fsm->state = aClock_normalMode;

		//update display
		scheduler_getTimeRTC(time);
		displayPrint_init();
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		break;

	case JS_BUTTON:
		//stop the buzzing and return to normal mode
		stopRedFlash();
		led_yellowOff();
		buzzer_stop();
		fsm->state = aClock_normalMode;

		//update display
		scheduler_getTimeRTC(time);
		displayPrint_init();
		printf("%u:%u:%u\nJoystick AL Set\nRotary AL On", time->hour,
				time->minute, time->second);
		break;

	default:
		break;

	}
}


/**
 * Starts flashing of the red led.
 * Stop with "stopRedFlash()".
 */
void startRedFlash(){
	led_redToggle();
	scheduler_add(startRedFlash, RED_FLASH_INTERVALL);
}

/**
 * Stops flashing of the red led.
 * Start with "startRedFlash()".
 */
void stopRedFlash(){
	scheduler_remove(startRedFlash);
}

/**
 * Calculates the clock hours of a ms value.
 *
 * @return the hour
 */
uint8_t getHours(uint32_t timeSet){
	return timeSet/HOUR_IN_MS;
}

/**
 * Sets the clock hour of a ms value.
 *
 * @param timeSet the ms value
 * @param hour the hour which should be set
 */
void setHours(uint32_t *timeSet, uint8_t hour){
	uint32_t remainder = 0;
	remainder = *timeSet % HOUR_IN_MS;
	*timeSet = (hour*HOUR_IN_MS) + remainder;
}

/**
 * Calculates the clock minutes of a ms value.
 *
 * @return the minute
 */
uint8_t getMinutes(uint32_t timeSet){
	return (timeSet%HOUR_IN_MS)/MINUTES_A_HOUR_IN_MS;
}

/**
 * Sets the clock minutes of a ms value.
 *
 * @param timeSet the ms value
 * @param minutes the minute which should be set
 */
void setMinutes(uint32_t *timeSet, uint8_t minutes){
	uint32_t hours = getHours(*timeSet);
	*timeSet = (hours*HOUR_IN_MS) + (minutes*MIN_IN_MS);
}
